﻿using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UniversalBlazor.CoreComponent;

namespace UniversalBlazor
{
    public class ComponentRouter : ComponentBase
    {
        private static IDictionary<string, Type> targetComponentMap = new Dictionary<string, Type>();
        private IReadOnlyDictionary<string, object> parameters;

        private Type targetComponent;
        private object componentInstace;

        static ComponentRouter()
        {
            if (GlobalConfig.ComponentConfig == null)
            {
                throw new TypeInitializationException("需要在 Startup 中调用 app.AddELement() 方法", null);
            }
            targetComponentMap = GlobalConfig.ComponentConfig.TargetComponents;
        }

        public override async Task SetParametersAsync(ParameterView parameters)
        {
            await base.SetParametersAsync(parameters);
            this.parameters = parameters.ToDictionary().ToDictionary(x => x.Key.ToLower(), x => x.Value);
            if (componentInstace != null)
            {
                await (Task)targetComponent?.GetMethod("SetParametersAsync").Invoke(componentInstace, new object[] { parameters });
            }
        }

        protected override void OnInitialized()
        {
            if (!targetComponentMap.TryGetValue(this.GetType().Name, out targetComponent))
            {
                throw new TypeInitializationException($"{this.GetType().Name}没有对应的 TargetComponent", null);
            }
            if (componentInstace != null)
            {
                targetComponent?.GetMethod("OnInitialized", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[0]);
            }
        }

        protected override async Task OnInitializedAsync()
        {
            if (componentInstace != null)
            {
                await (Task)targetComponent?.GetMethod("OnInitializedAsync", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[0]);
            }
        }

        protected override void OnParametersSet()
        {
            if (componentInstace != null)
            {
                targetComponent?.GetMethod("OnParametersSet", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[0]);
            }
        }

        protected override Task OnParametersSetAsync()
        {
            if (componentInstace != null)
            {
                return (Task)targetComponent?.GetMethod("OnParametersSetAsync", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[0]);
            }
            return Task.CompletedTask;
        }

        protected override bool ShouldRender()
        {
            if (componentInstace != null)
            {
                return (bool)targetComponent?.GetMethod("ShouldRender", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[0]);
            }
            return true;
        }

        private bool TryGetAttributeValue(PropertyInfo property, out object value)
        {
            return this.parameters.TryGetValue(property.Name.ToLower(), out value);
            //value = null;
            //if (Attributes.TryGetValue(property.Name, out value))
            //{
            //    return true;
            //}
            //if (property.IsDefined(typeof(ParameterAttribute)))
            //{
            //    value = property.GetValue(this);
            //    return true;
            //}
            //return false;
        }

        protected override void BuildRenderTree(RenderTreeBuilder builder)
        {
            if (targetComponent == null)
            {
                return;
            }
            builder.OpenComponent(0, targetComponent);
            foreach (var property in targetComponent.GetProperties())
            {
                if (!TryGetAttributeValue(property, out var value))
                {
                    continue;
                }

                if (false && value is EventCallback callback)
                {
                    builder.AddAttribute(1, property.Name, callback);
                }
                else
                {
                    builder.AddAttribute(2, property.Name, value);
                }
            }

            builder.AddComponentReferenceCapture(2, obj => componentInstace = obj);
            builder.CloseComponent();
        }

        protected override void OnAfterRender(bool firstRender)
        {
            if (componentInstace != null)
            {
                targetComponent?.GetMethod("OnAfterRender", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[] { firstRender });
            }
        }

        protected override Task OnAfterRenderAsync(bool firstRender)
        {
            if (componentInstace != null)
            {
                return (Task)targetComponent?.GetMethod("OnAfterRenderAsync", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(componentInstace, new object[] { firstRender });
            }
            return Task.CompletedTask;
        }

        [Parameter(CaptureUnmatchedValues = true)]
        public IReadOnlyDictionary<string, object> Attributes { get; set; } = new Dictionary<string, object>();
    }
}
